package tankgame6;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.io.FileReader;
import java.util.Vector;

// "每时每刻"要重新画出 坦克，子弹
@SuppressWarnings("all")
public class MyPanel extends JPanel implements KeyListener, Runnable {

    SuperMan tank = null;
    //敌人坦克，每个坦克在自己线程独自运行，所以用Vector（线程安全）
    Vector<Enemy> enemies = new Vector<>();

    //  [{x y dir},{x y dir}]  放着敌人坦克们 的坐标
    Vector<Node> nodes = new Vector<>();

    Vector<Bomb> bombs = new Vector<Bomb>();//炸弹集合
    Image image1 = null;//三个爆炸图片效果
    Image image2 = null;
    Image image3 = null;

    int enemySize = 5;

    /**
     * new对象
     * 启动线程
     * 取数据，显示杀敌人数
     * 加载音乐
     */
    public MyPanel(String key) {
        //避免没有record文件的错误
        File file = new File(Recorder.getRecordFile());
        if (file.exists()) {
            nodes = Recorder.getNodsAndEnemyTankNum();
            // 一个敌人坦克都没有了，就重新开始游戏
            if (nodes.size() == 0){
                key = "1";
                System.out.println("上局敌人被打完了，重新自动开始新游戏");
            }
        } else {
            System.out.println("没有上一把游戏");
            key = "1";
        }
        Recorder.setEnemies(enemies);//记录战绩

        tank = new SuperMan(100, 400);//初始化我方坦克
        tank.setSpeed(3);

        // 初始化敌人坦克
        switch (key) {
            case "1":
                //新游戏
                for (int i = 0; i < enemySize; i++) {
                    Enemy enemy = new Enemy(100 * (i + 1), 0);
                    enemy.setDirect(0);
                    //把"敌人坦克集合"给"敌人坦克对象"的“坦克集”属性
                    enemy.setEnemies(enemies);
                    enemies.add(enemy);//敌人坦克集合
                    new Thread(enemy).start();//然后坦克线程run自己又去常见子弹对象，紧接着子弹线程start
                }
                break;
            case "2":
                //继续游戏
                for (int i = 0; i < nodes.size(); i++) {
                    Node node = nodes.get(i);
                    Enemy enemy = new Enemy(node.getX(), node.getY(),node.getDirect());

                    //把"敌人坦克集合"给"敌人坦克对象"的“坦克集”属性
                    enemy.setEnemies(enemies);
                    enemies.add(enemy);//敌人坦克集合
                    new Thread(enemy).start();//每个敌人坦克都是线程。创建了当然要启动

                    //初始化敌人坦克子弹   (坦克对象run自动会创建子弹对象，这里不用额外再写)
//                    Shot shot = new Shot(enemy.getX() + 20, enemy.getY() + 60, enemy.getDirect());
//                    enemy.shots.add(shot);
//                    new Thread(shot).start();//启动shot线程
                }
                break;
            default:
                System.out.println("输入不对哦~");
        }

        //坦克加载完，再播放音乐
        new AePlayWave("src\\111.wav").start();
    }//paint()

    /**
     * @param x
     * @param y
     * @param g      画笔
     * @param direct 坦克上下左右方向
     * @param type   敌方，我方坦克
     */
    public void drawTank(int x, int y, Graphics g, int direct, int type) {
        switch (type) {
            case 0://我方坦克
                g.setColor(Color.cyan);//青色
                break;
            case 1://敌方坦克
                g.setColor(Color.yellow);//控制所有图形的颜色
                break;
        }
        //不同方向，坦克样子都要画！麻烦哦
        //0-上  1-右  2-下 3-左
        switch (direct) {
            case 0://朝上的坦克
                g.fill3DRect(x, y, 10, 60, false);//左边轮子
                g.fill3DRect(x + 30, y, 10, 60, false);//右边轮子
                g.fill3DRect(x + 10, y + 10, 20, 40, false);//中间方形
                g.fillOval(x + 10, y + 20, 20, 20);//中间圆圈
                g.drawLine(x + 20, y + 30, x + 20, y - 10);//炮筒
                break;
            case 1://朝右的坦克
                g.fill3DRect(x, y, 60, 10, false);//上边轮子
                g.fill3DRect(x, y + 30, 60, 10, false);//下边轮子
                g.fill3DRect(x + 10, y + 10, 40, 20, false);//中间方形
                g.fillOval(x + 20, y + 10, 20, 20);//中间圆圈
                g.drawLine(x + 30, y + 20, x + 60, y + 20);//炮筒
                break;
            case 2://朝下的坦克
                g.fill3DRect(x, y, 10, 60, false);//左边轮子
                g.fill3DRect(x + 30, y, 10, 60, false);//右边轮子
                g.fill3DRect(x + 10, y + 10, 20, 40, false);//中间方形
                g.fillOval(x + 10, y + 20, 20, 20);//中间圆圈
                g.drawLine(x + 20, y + 30, x + 20, y + 60);//炮筒
                break;
            case 3://朝左的坦克
                g.fill3DRect(x, y, 60, 10, false);//上边轮子
                g.fill3DRect(x, y + 30, 60, 10, false);//下边轮子
                g.fill3DRect(x + 10, y + 10, 40, 20, false);//中间方形
                g.fillOval(x + 20, y + 10, 20, 20);//中间圆圈
                g.drawLine(x + 30, y + 20, x, y + 20);//炮筒
                break;
        }
    }//drawTank

    /**
     * 细节：
     * 1，只要面板被最小化，人类不再看，计算机就偷懒不再画，就不再执行paint方法（重绘）！！！！
     * 但是坦克，子弹的数据因为线程的原因，背地里还是在计算
     * <p>
     * 2，现实世界的消失，在计算里的实现方法是：
     * 计算机每次在集合里，画出每一个对象，如果要消失，本质上就是对象不在集合而已
     * eg：刚刚在---->不再
     * 原理： 刚刚在集合里，后台在集合里把对象删除，下次画时候没有原来的对象，就不会画，相当于消失
     * <p>
     * <p>
     * 总结：
     * 1，后台坐标的计算，和画出来的时间不同步
     * <p>
     * 2,画的任何东西，都是get()得到每个对象的属性值，所以每次重绘以后都是当时最新的数据
     * （因为数据的“set”和“get”用的都是对象的属性值）
     */
    @Override
    public void paint(Graphics g) {
        //继承父类方法
        super.paint(g);
        g.fillRect(0, 0, 1000, 650);//填冲矩形，默认黑色（就是面板上全是黑色而已）
        showInfo(g);    //右边显示战绩

        /**
         * 画我们的坦克
         *
         * 细节：      （坦克数据的"改变set"和"拿取get"都是对象的属性！！！）
         *         后台数据在tank对象属性里，所以数据变化后还是在对象属性里，
         *         所以这里相当于每次刷线坦克位置了！！！！！！！！后面keyPressed不用this.repaint()
         */
        // hitTank(Shot s, Tank tank)会把我方坦克置false ，false就不画了，相当于消失
        if (tank.isLive)
            drawTank(tank.getX(), tank.getY(), g, tank.getDirect(), 0);

        /**
         *   画我们的子弹
         *
         *   画子弹，不仅是画子弹，还会把“消失”的子弹，从集合里移除掉
         */
        for (int i = 0; i < tank.shots.size(); i++) {
            Shot shot = tank.shots.get(i);
            if (shot.isLive) {
                g.draw3DRect(shot.x, shot.y, 3, 3, false);
            } else if (shot.isLive == false) {
                tank.shots.remove(shot);
            } // 也没问题呀

            // 感觉shot不会是null呀
           /*if (shot != null && shot.isLive) {
                g.draw3DRect(shot.x, shot.y, 3, 3, false);
            } else if (shot.isLive == false && shot != null) {
                tank.shots.remove(shot);
            }*/
        }

        /**
         * 画敌人坦克,子弹
         *
         *      画子弹，不仅是画子弹，还会把“消失”的子弹，从集合里移除掉
         */
        for (int i = 0; i < enemies.size(); i++) {
            Enemy enemy = enemies.get(i);
            if (enemy.isLive) {
                //都是取得都是新数据，因为数据的变化都在对象的属性里面。
                drawTank(enemy.getX(), enemy.getY(), g, enemy.getDirect(), 1);
                for (int j = 0; j < enemy.shots.size(); j++) {
                    Shot shot = enemy.shots.get(j);
                    if (shot.isLive) {
                        g.draw3DRect(shot.x, shot.y, 3, 3, false);
                    } else {
                        enemy.shots.remove(shot);   //子弹集合里去掉已经被false标记的对象
                    }
                }
            }
//            else {
//                enemies.remove(enemy);//hitTank（）里有，所以这里不用了
//            }
        }

        //画爆炸效果
        //bombs集合有,就画
        for (int i = 0; i < bombs.size(); i++) {
            Bomb bomb = bombs.get(i);
            if (bomb.life > 6) {
                g.drawImage(image1, bomb.x, bomb.y, 60, 60, this);
            } else if (bomb.life > 3) {
                g.drawImage(image2, bomb.x, bomb.y, 60, 60, this);
            } else {
                g.drawImage(image3, bomb.x, bomb.y, 60, 60, this);
            }
            bomb.downLife();//减少生命周期
            if (bomb.life == 0)
                bombs.remove(bomb);
        }

//        System.out.println("===========================");
//        image1 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("bomb_1.gif"));
//        image1 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("bomb_2.gif"));
//        image1 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("bomb_3.gif"));

        //初始化爆炸图片
//        image1=Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("bomb_1.gif"));
//        image2=Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("bomb_2.gif"));
//        image3=Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("bomb_3.gif"));
    }//paint

    //右边画战绩
    public void showInfo(Graphics g) {
        g.setColor(Color.black);
        Font font = new Font("宋体", Font.BOLD, 25);
        g.setFont(font);
        g.drawString("击毁坦克数目：", 1020, 40);

        drawTank(1020, 60, g, 0, 0);
        g.setColor(Color.RED);
        g.drawString(Recorder.getAllEnemyTankNum() + "", 1080, 100);
    }

    /**
     * 要做的事情：
     * 1，rePaint()
     * 动画原理：
     * 本质就是重复画画，重复展示。时间间隔很紧凑（100ms），
     * 前后的画右特别“像”
     * 所以以为是一个东西，连续做动作
     * 其实早已经是不同的东西了，无数个东西合在一起快速播放，就像是一个东西在做连续动作
     * <p>
     * （宏观现象：子弹，坦克在移动）
     * <p>
     * <p>
     * 2,在这1ms时间内，可能会把子弹，坦克对象从集合里删除掉，使得下一秒repaint后，因为没有此对象而不会画出
     * <p>
     * （宏观现象：子弹，坦克在符合实际经验下消失不见）
     * <p>
     * <p>
     * 3，线程休眠结果：
     * 每隔1ms，反复的执行run代码
     */
    @Override
    public void run() {
        while (true) {
//            继承了JPanel，所以paint(Graphics g)会自动执行，他是来画东西的
            //隔100ms，根据新的“坐标”，再画一次。且覆盖原来的画。

              /*
                    计算机画的画每次仅仅展示1ms，时间一到，立刻展示其他画
                    eg：1s就把1000张图按照先后顺序一个一个展示，
                        人脑反应慢，刚刚看见的图片脑子还哟画面，然后看到下一个图片，人脑会快速的把两个照片做联系
                                以此类推，人脑在1s内把1000个特别相似的照片联系起来，
                                就会在人脑力形成”连续不断“的动画片


                         note：1，静止的东西也是一直在相同位置，画相同的东西。本质上会出现空白
                                2，一个画面显示2s再画另一幅图，前后图变化大，就不会有“连续的错觉”
                                3，所以展示图越频繁，“连续”视觉越好
            面板一直判断，“子弹”有没有打到我们或者敌人身上。
            一直把子弹和坦克isLive=>false，在画的时候看见子弹，坦克isLive=false 或者对象为null就不再画
             */
            try {
                Thread.sleep(50);//图片展示的越频繁，1s就有1000张图片展示出来
                hitSuperman();
                hitEnemys();
                this.repaint();//本质是执行paint(Graphics g)方法。    虽然画面不更新，但是坦克行为一直背地里在变化。
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }//while
    }//run

    /*
            敌人坦克是否打到我们坦克上
     */
    public void hitSuperman() {
        for (int i = 0; i < enemies.size(); i++) {
            Enemy enemy = enemies.get(i);
            for (int j = 0; j < enemy.shots.size(); j++) {
                Shot shot = enemy.shots.get(j);
                if (tank.isLive && shot.isLive) {
                    hitTank(shot, tank);
                }
            }
        }
    }//hitSuperman

    /*
            我们发射的子弹有没有打中敌人
     */
    public void hitEnemys() {
        for (int i = 0; i < tank.shots.size(); i++) {
            Shot shot = tank.shots.get(i);
            if (shot.isLive) {  //自己子弹销毁了没必要判断
                for (int j = 0; j < enemies.size(); j++) {
                    Enemy enemy = enemies.get(j);
                    hitTank(tank.shot, enemy);
                }
            }
        }
    }   //hitEnemys()

    /**
     * 结果：
     * 判断出子弹坐标已经在坦克的区域里，
     * 就会把子弹和坦克的isLive--->false
     * 并且从集合里删除，在下次画的时候就不画，相当于现实世界的“消失”
     * <p>
     * 疑惑：
     * 直接从集合里删除就行，为什么还要用isLive判断。我感觉用两个累赘
     * 直接在集合里删除就行。因为画的时候也是在集合里找对象
     */
    public void hitTank(Shot s, Tank tank) {
        //判断s 击中坦克
        switch (tank.getDirect()) {
            case 0: //坦克向上
            case 2: //坦克向下
                if (s.x > tank.getX() && s.x < tank.getX() + 40
                        && s.y > tank.getY() && s.y < tank.getY() + 60) {
                    s.isLive = false;
                    tank.isLive = false;
                    if (tank instanceof Enemy) {
                        enemies.remove(tank);
                        //当我方击毁一个敌人坦克时，就对数据allEnemyTankNum++
                        Recorder.addallEnemyTankNum();
                    }//if
                    // 如果是我们的坦克，就画不出来了。直接就死掉
                }
                break;
            case 1: //坦克向右
            case 3: //坦克向左
                if (s.x > tank.getX() && s.x < tank.getX() + 60
                        && s.y > tank.getY() && s.y < tank.getY() + 40) {
                    s.isLive = false;
                    tank.isLive = false;
                    if (tank instanceof Enemy) {
                        enemies.remove(tank);
                        Recorder.addallEnemyTankNum();
                    }
                }
                // 如果是我们的坦克，就画不出来了。直接就死掉
                break;
        }
    }


    @Override
    public void keyTyped(KeyEvent e) {

    }//keyTyped

    /**
     * 按键设置对象方向，
     * 但是如果出了画面，这里删除
     */
    @Override
    public void keyPressed(KeyEvent e) {
//        System.out.println("keyPressed");
        if (e.getKeyCode() == KeyEvent.VK_W) {    // w键
            tank.setDirect(0);
            if (tank.getY() > 0)
                tank.moveUp();
            else return;//就是w键没效果
        } else if (e.getKeyCode() == KeyEvent.VK_S) {
            tank.setDirect(2);
            if (tank.getY() + 60 < 650)
                tank.moveDown();
            else return;//就是w键没效果 ,不写此代码和写此代码效果一样
        } else if (e.getKeyCode() == KeyEvent.VK_A) {
            tank.setDirect(3);
            if (tank.getX() > 0)
                tank.moveLeft();
            else return;//就是w键没效果

        } else if (e.getKeyCode() == KeyEvent.VK_D) {
            tank.setDirect(1);
            if (tank.getX() + 60 < 1000)
                tank.moveRight();
            else return;//就是w键没效果

        }
        //J键我方坦克创建子弹线程
        if (e.getKeyCode() == KeyEvent.VK_J) {
            // 如果我方坦克死了，这里不判断，会在死亡位置，继续new出子弹对象
            if (tank.isLive)
                tank.shotEnemyTank();
        }
//        this.repaint();        //注释也不影响？？？？因为：
    }//keyPressed

    @Override
    public void keyReleased(KeyEvent e) {

    }

}
